package org.ikun.security_demo;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.ikun.security_demo.model.ResBean;
import org.ikun.security_demo.service.UserService1;
import org.ikun.security_demo.service.UserService2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.argon2.Argon2PasswordEncoder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.DelegatingPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.crypto.password.Pbkdf2PasswordEncoder;
import org.springframework.security.crypto.scrypt.SCryptPasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.session.HttpSessionEventPublisher;

import java.util.HashMap;
import java.util.Map;

@Configuration
@EnableWebSecurity
public class SpringSecurityConfig  {

    @Autowired
    UserService1 userService1;

    @Autowired
    UserService2 userService2;


    @Bean
    UserDetailsService u1(){
        InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
        manager.createUser(User.withUsername("lisi").password("{noop}123456").roles("admin").build());
        return manager;
    }

    @Bean
    SecurityFilterChain securityFilterChain1(HttpSecurity httpSecurity)  throws Exception{
        httpSecurity.antMatcher("/web/**")
                .userDetailsService(userService1)
                .authorizeRequests()
                .anyRequest()
                .authenticated()
                .and()
                .formLogin()
                .loginPage("/login.html")
                //配置处理登录请求的接口,其实就是配置了过滤器里的拦截规则，将来的登录请求就会在过滤器中被处理
                .loginProcessingUrl("/web/login")
                //配置登录表单中用户名的 key
                .usernameParameter("username")
                //配置登录表单中的密码 默认也是username 和 password
                .passwordParameter("password")
                .successHandler((request, response, authentication) -> {
                    response.setContentType("application/json;charset=utf-8");
                    ResBean resBean = ResBean.ok("登陆成功web");
                    String s = new ObjectMapper().writeValueAsString(resBean);
                    response.getWriter().write(s);
                })
                .and()
                .sessionManagement()
                //设置会话的最大并发数
                .maximumSessions(1)
                //达到最大登录数后，是否要阻止下一个登录
                .maxSessionsPreventsLogin(true)
                //关闭默认的csrf认证
                .and()
                .and()
                .csrf().disable();
        return httpSecurity.build();
    }
    @Bean
    SecurityFilterChain securityFilterChain2(HttpSecurity httpSecurity)  throws Exception{
        httpSecurity.antMatcher("/v1/**")
                .userDetailsService(u1())
                .authorizeRequests()
                .anyRequest()
                .authenticated()
                .and()
                //开始配置登录表单
                .formLogin()
                //配置登录页面，如果访问了一个需要登录以后才能访问的页面，那么就会自动登录到这个页面
                .loginPage("/login.html")
                //配置处理登录请求的接口,其实就是配置了过滤器里的拦截规则，将来的登录请求就会在过滤器中被处理
                .loginProcessingUrl("/v1/login")
                //配置登录表单中用户名的 key
                .usernameParameter("username")
                //配置登录表单中的密码 默认也是username 和 password
                .passwordParameter("password")
                //配置登录成功后访问的接口
                .successHandler((request, response, authentication) -> {
                    response.setContentType("application/json;charset=utf-8");
                    ResBean resBean = ResBean.ok("登陆成功v1");
                    String s = new ObjectMapper().writeValueAsString(resBean);
                    response.getWriter().write(s);
                })
                .and()
                .sessionManagement().disable()
                //关闭默认的csrf认证
                .csrf().disable();
        return httpSecurity.build();
    }

    @Bean
    PasswordEncoder passwordEncoder(){
        String encodingId = "bcrypt";
        Map<String, PasswordEncoder> encoders = new HashMap<>();
        encoders.put(encodingId, new BCryptPasswordEncoder());
        encoders.put("ldap", new org.springframework.security.crypto.password.LdapShaPasswordEncoder());
        encoders.put("MD4", new org.springframework.security.crypto.password.Md4PasswordEncoder());
        encoders.put("MD5", new org.springframework.security.crypto.password.MessageDigestPasswordEncoder("MD5"));
        encoders.put("noop", org.springframework.security.crypto.password.NoOpPasswordEncoder.getInstance());
        encoders.put("pbkdf2", new Pbkdf2PasswordEncoder());
        encoders.put("scrypt", new SCryptPasswordEncoder());
        encoders.put("SHA-1", new org.springframework.security.crypto.password.MessageDigestPasswordEncoder("SHA-1"));
        encoders.put("SHA-256",
                new org.springframework.security.crypto.password.MessageDigestPasswordEncoder("SHA-256"));
        encoders.put("sha256", new org.springframework.security.crypto.password.StandardPasswordEncoder());
        encoders.put("argon2", new Argon2PasswordEncoder());
        return new DelegatingPasswordEncoder("MD5", encoders);
    }

    //@Override
    protected void configure(HttpSecurity http) throws Exception {
        //开始认证
        //设置登录,注销，表单登录不用拦截，其他请求要拦截
        http.authorizeRequests()
                .antMatchers("/login.html")
                .anonymous()
                //所有的请求，类似于shiro中的 /**
                .anyRequest()
                //必须要认证之后才能访问，类似于shiro的authc
                .authenticated()

                .and()
                //默认情况下，开启了remember-me以后，默认所有接口都可以通过remember-me访问
                //开始配置登录表单
                .formLogin()
                //配置登录页面，如果访问了一个需要登录以后才能访问的页面，那么就会自动登录到这个页面
                .loginPage("/login.html")
                //配置处理登录请求的接口,其实就是配置了过滤器里的拦截规则，将来的登录请求就会在过滤器中被处理
                .loginProcessingUrl("/doLogin")
                //配置登录表单中用户名的 key
                .usernameParameter("username")
                //配置登录表单中的密码 默认也是username 和 password
                .passwordParameter("password")
                //配置登录成功后访问的接口
                .defaultSuccessUrl("/hello")
                //登录失败后访问的接口
                .failureUrl("/login.html")
                //登录成功处理器
//                .successHandler((request, response, authentication) -> {
//                    response.setContentType("application/json;charset=utf-8");
//                    ResBean resBean = ResBean.ok("登陆成功", authentication.getPrincipal());//getPrincipal()就是你的用户对象
//                    String s = new ObjectMapper().writeValueAsString(resBean);
//                    response.getWriter().write(s);
//                })
//                .failureHandler((request, response, exception) -> {
//                    //登录失败可能有很多种原因
//
//                    response.setContentType("application/json;charset=utf-8");
//                    ResBean resBean = ResBean.error("登陆失败");
//                    if (exception instanceof BadCredentialsException){
//                        resBean.setMsg("用户名或密码输入错误，登录失败");
//                    }else if (exception instanceof UsernameNotFoundException){
//                        //这个异常默认隐藏了，所以这个分支不会进来，怕有人试密码
//                    }else if (exception instanceof LockedException){
//                        resBean.setMsg("账户被锁定，登录失败");
//                    }else if (exception instanceof AccountExpiredException){
//                        resBean.setMsg("账户过期，登录失败");
//                    }else if (exception instanceof CredentialsExpiredException){
//                        resBean.setMsg("密码过期，登录失败");
//                    }else if (exception instanceof DisabledException){
//                        resBean.setMsg("账户被禁用，登录失败");
//                    }
//                    String s = new ObjectMapper().writeValueAsString(resBean);
//                    response.getWriter().write(s);
//                })
                .and()
                .exceptionHandling()
                //用户未登录就访问某一个页面，就会触发这个方法
//                .authenticationEntryPoint((request, response, authException) -> {
//                    response.setContentType("application/json;charset=utf-8");
//                    ResBean resBean = ResBean.error("尚未登录，请登录");
//                    String s = new ObjectMapper().writeValueAsString(resBean);
//                    response.getWriter().write(s);
//                })
                .and()
                .sessionManagement()
                //设置会话的最大并发数
                .maximumSessions(1)
                //达到最大登录数后，是否要阻止下一个登录
                .maxSessionsPreventsLogin(true)
                //关闭默认的csrf认证
                .and()
                .and()
                .csrf().disable();

    }

    /**
     * 这个事件，可以监听到HttpSession 的销毁操作，当有httpsession销毁时，就将销毁事件广播出去，map收到通知，移除对应的会话信息。
     * @return
     */
    @Bean
    HttpSessionEventPublisher httpSessionEventPublisher(){
        return new HttpSessionEventPublisher();
    }
}
